/*
 * @Author: Snow
 * @Date: 2021-10-19 16:03:24
 * @Description: 天空盒
 */
import { Matrix4 } from "../../utils/Matrix4";
import { Util } from "../../utils/Util";
import { WebGLUtil } from "../../utils/WebGLUtil";
import { IProgramInfo } from "../interface/IDefine";
import { Render } from "../Render";
import { WebglTemplateBase } from "../template/WebglTemplateBase";
export class TextureSkyBox extends WebglTemplateBase {

    private clipProgramInfo: IProgramInfo;

    protected override initProgram(): void {
        this.vsSource = `
            attribute vec4 a_position;
            attribute vec3 a_normal;

            uniform mat4 u_projection;
            uniform mat4 u_view;
            uniform mat4 u_world;
            
            varying vec3 v_worldPosition;
            varying vec3 v_worldNormal;
            
            void main() {
                gl_Position = u_projection * u_view * u_world * a_position;
                v_worldPosition = (u_world * a_position).xyz;
                v_worldNormal = normalize(mat3(u_world) * a_normal);
            }
        `;

        this.fsSource = `
            precision mediump float;

            varying vec3 v_worldPosition;
            varying vec3 v_worldNormal;
            
            // 纹理。
            uniform samplerCube u_texture;

            // 相机位置。
            uniform vec3 u_worldCameraPosition;
            
            void main() {
                vec3 worldNormal = normalize(v_worldNormal);
                
                vec3 eyeToSurfaceDir = normalize(v_worldPosition - u_worldCameraPosition);
                vec3 direction = reflect(eyeToSurfaceDir,worldNormal);

                gl_FragColor = textureCube(u_texture, direction);
            }
        `;

        let clipVsSource = `
            attribute vec4 a_position;
            varying vec4 v_position;
            
            void main() {
                v_position = a_position;
                gl_Position = vec4(a_position.xy,1,1);
                //gl_Position.z = 1;
            }
        `;

        let clipFsSource = `
            precision mediump float;

            varying vec4 v_position;
            
            //天空盒纹理
            uniform samplerCube u_skyBox;

            //视图方向投影逆矩阵
            uniform mat4 u_viewDirectionProjectionInverse;
            
            void main() {
                vec4 temp  = u_viewDirectionProjectionInverse * v_position;
                
                gl_FragColor = textureCube(u_skyBox, normalize(temp.xyz / temp.w));
                //gl_FragColor = vec4(1,0,0,1);
            }
        `
        super.initProgram();
        this.clipProgramInfo = WebGLUtil.createProgramInfo(Render.GL, clipVsSource, clipFsSource);
    }

    protected override initBuffers(): void {
        super.initBuffers();
        const gl = Render.GL;

        //裁剪空间区域
        let clipPos = [
            -1, -1,
            1, -1,
            -1, 1,
            -1, 1,
            1, -1,
            1, 1,
        ]

        var positions = [
            -0.5, -0.5, -0.5,
            -0.5, 0.5, -0.5,
            0.5, -0.5, -0.5,
            -0.5, 0.5, -0.5,
            0.5, 0.5, -0.5,
            0.5, -0.5, -0.5,

            -0.5, -0.5, 0.5,
            0.5, -0.5, 0.5,
            -0.5, 0.5, 0.5,
            -0.5, 0.5, 0.5,
            0.5, -0.5, 0.5,
            0.5, 0.5, 0.5,

            -0.5, 0.5, -0.5,
            -0.5, 0.5, 0.5,
            0.5, 0.5, -0.5,
            -0.5, 0.5, 0.5,
            0.5, 0.5, 0.5,
            0.5, 0.5, -0.5,

            -0.5, -0.5, -0.5,
            0.5, -0.5, -0.5,
            -0.5, -0.5, 0.5,
            -0.5, -0.5, 0.5,
            0.5, -0.5, -0.5,
            0.5, -0.5, 0.5,

            -0.5, -0.5, -0.5,
            -0.5, -0.5, 0.5,
            -0.5, 0.5, -0.5,
            -0.5, -0.5, 0.5,
            -0.5, 0.5, 0.5,
            -0.5, 0.5, -0.5,

            0.5, -0.5, -0.5,
            0.5, 0.5, -0.5,
            0.5, -0.5, 0.5,
            0.5, -0.5, 0.5,
            0.5, 0.5, -0.5,
            0.5, 0.5, 0.5,
        ];

        ///法向量
        let normals = [
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,
            0, 0, -1,

            0, 0, 1,
            0, 0, 1,
            0, 0, 1,
            0, 0, 1,
            0, 0, 1,
            0, 0, 1,

            0, 1, 0,
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,
            0, 1, 0,

            0, -1, 0,
            0, -1, 0,
            0, -1, 0,
            0, -1, 0,
            0, -1, 0,
            0, -1, 0,

            -1, 0, 0,
            -1, 0, 0,
            -1, 0, 0,
            -1, 0, 0,
            -1, 0, 0,
            -1, 0, 0,

            1, 0, 0,
            1, 0, 0,
            1, 0, 0,
            1, 0, 0,
            1, 0, 0,
            1, 0, 0,
        ];

        // Create a texture.
        let textureCubeMap = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_CUBE_MAP, textureCubeMap);

        const faceInfos = [
            { target: gl.TEXTURE_CUBE_MAP_POSITIVE_X, url: "res/pos-x.jpg" },
            { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_X, url: "res/neg-x.jpg" },
            { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Y, url: "res/pos-y.jpg" },
            { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, url: "res/neg-y.jpg" },
            { target: gl.TEXTURE_CUBE_MAP_POSITIVE_Z, url: "res/pos-z.jpg" },
            { target: gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, url: "res/neg-z.jpg" },
        ];
        faceInfos.forEach((faceInfo) => {
            const { target, url } = faceInfo;
            // 上传画布到立方体贴图的每个面。
            const level = 0;
            const internalFormat = gl.RGBA;
            const format = gl.RGBA;
            const type = gl.UNSIGNED_BYTE;
            const width = 512;
            const height = 512;
            gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);

            const img = new Image();
            img.src = url;
            img.addEventListener("load", () => {
                gl.bindTexture(gl.TEXTURE_CUBE_MAP, textureCubeMap);
                gl.texImage2D(target, level, internalFormat, format, type, img);
                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
            })
        });

        gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
        gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);

        let cameraAngleRadians = Util.degToRad(0);
        let fieldOfViewRadians = Util.degToRad(60);
        let cameraHeight = 50;

        let drawScene = () => {
            gl.enable(gl.CULL_FACE);
            gl.enable(gl.DEPTH_TEST);
            let time = this.ts;
            time = time;
            WebGLUtil.resizeCanvasToMatchDisplaySize(gl);
            // Clear the canvas AND the depth buffer.
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);


            // Compute the projection matrix
            let aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
            var projectionMatrix = Matrix4.perspective(fieldOfViewRadians, aspect, 1, 2000);

            // Compute the camera's matrix using look at.
            let xSpeed = this.uiParam.x;
            let mm = Matrix4.rotateY(time * 0.002 * xSpeed);
            mm = Matrix4.multiply(mm, Matrix4.translation(0, 0, 2));

            let cameraPosition = [mm[12], mm[13], mm[14]];
            let target = [0, 0, 0];
            let up = [0, 1, 0];
            let cameraMatrix = Matrix4.lookAt(cameraPosition, target, up);

            // Make a view matrix from the camera matrix.
            let viewMatrix = Matrix4.invert(cameraMatrix);

            let viewProjectionMatrix = Matrix4.multiply(projectionMatrix, viewMatrix);


            let cubeXRotation = -time / 5;
            let cubeYRotation = time / 5;

            let mat1 = Matrix4.rotateX(cubeXRotation);
            let mat2 = Matrix4.rotateY(cubeYRotation);

            //let worldMatrix = Matrix4.multiply(mat1, mat2);
            let worldMatrix = Matrix4.identity();

            ///// 先绘制立方体
            // draw the cube
            gl.useProgram(this.programInfo.program);
            
            let posLocation = gl.getAttribLocation(this.programInfo.program, "a_position");
            gl.enableVertexAttribArray(posLocation);
            let posBuffer = gl.createBuffer();  
            gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
            gl.vertexAttribPointer(posLocation, 3, gl.FLOAT, false, 0, 0);
    
            let normalBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);
    
            let normalLocation = gl.getAttribLocation(this.programInfo.program, "a_normal");
            gl.enableVertexAttribArray(normalLocation);
            gl.vertexAttribPointer(normalLocation, 3, gl.FLOAT, false, 0, 0);


            let texUnforms = gl.getUniformLocation(this.programInfo.program, "u_texture");
            gl.uniform1i(texUnforms, 0);
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, textureCubeMap);

            let projectLocation = gl.getUniformLocation(this.programInfo.program, "u_projection")
            let viewLocation = gl.getUniformLocation(this.programInfo.program, "u_view")
            let worldLocation = gl.getUniformLocation(this.programInfo.program, "u_world")

            gl.uniformMatrix4fv(projectLocation, false, new Float32Array(projectionMatrix));
            gl.uniformMatrix4fv(viewLocation, false, new Float32Array(viewMatrix));
            gl.uniformMatrix4fv(worldLocation, false, new Float32Array(worldMatrix));

            let worldCameraPosition = gl.getUniformLocation(this.programInfo.program, "u_worldCameraPosition");
            gl.uniform3fv(worldCameraPosition, new Float32Array(cameraPosition));
            gl.depthFunc(gl.LESS);  // use the default depth test
            gl.drawArrays(gl.TRIANGLES, 0, 6 * 6);

            //先绘制天空盒
            // let our quad pass the depth test at 1.0
           
            gl.useProgram(this.clipProgramInfo.program);

            let clipBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, clipBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(clipPos), gl.STATIC_DRAW);

            
            let clipLocaion = gl.getAttribLocation(this.clipProgramInfo.program, "a_position");
            gl.enableVertexAttribArray(clipLocaion);
            gl.vertexAttribPointer(clipLocaion, 2, gl.FLOAT, false, 0, 0);


            let skyBoxUnforms = gl.getUniformLocation(this.clipProgramInfo.program, "u_skyBox");
            gl.uniform1i(skyBoxUnforms, 0);
            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_CUBE_MAP, textureCubeMap);


          

            let viewMatrix1 = Matrix4.invert(cameraMatrix);
            ///只关注方向  去掉位移
            viewMatrix1[12] = 0;
            viewMatrix1[13] = 0;
            viewMatrix1[14] = 0;

            let viewDirectionProjectionInverse = Matrix4.multiply(projectionMatrix, viewMatrix1);
            viewDirectionProjectionInverse = Matrix4.invert(viewDirectionProjectionInverse);
            let viewDirectionProjectionInverseLocation = gl.getUniformLocation(this.clipProgramInfo.program, "u_viewDirectionProjectionInverse");
            gl.uniformMatrix4fv(viewDirectionProjectionInverseLocation, false, new Float32Array(viewDirectionProjectionInverse));
            gl.depthFunc(gl.LEQUAL);
            gl.drawArrays(gl.TRIANGLES, 0, 6);
        }
        this.draw = drawScene;
    }

    private generateFace(ctx: CanvasRenderingContext2D, faceColor: string, textColor: string, text: string): void {
        const { width, height } = ctx.canvas;
        ctx.fillStyle = faceColor;
        ctx.fillRect(0, 0, width, height);
        ctx.font = `${width * 0.7}px sans-serif`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillStyle = textColor;
        ctx.fillText(text, width / 2, height / 2);
    }
}